跳到主要内容

Spring 中读取文件

Java资源路径的问题

参考资料 Java 项目读取 resources 资源文件路径那点事

实际上可以通过类加载路径来取得资源加载路径

System.out.println(Temp.class.getResource(""));
// file:/D:/JavaProject/study-java/studySocket/target/classes/com/alsritter/
System.out.println(Temp.class.getClassLoader().getResource(""));
// file:/D:/JavaProject/study-java/studySocket/target/classes/

所以要读取项目内的文件,一般是通过获取类加载路径的方式来取得资源路径,其实可以直接取得路径,无需先调用类加载器

// 直接这样写就好了
InputStream fileReader = JdbcUtils.class.getResourceAsStream("/urlPath.properties");

class.getResourceAStream()class.getClassLoader().getResorceAsStream() 的区别

1) InputStream inStream = PropertiesTest.class.getResourceAsStream("test.properties");

​2) inStream = PropertiesTest.class.getResourceAsStream("/com/test/demo/test.properties")

3) inStream = PropertiesTest.class.getClassLoader().getResourceAsStream("com/test/demo/test.properties");


```java
// 之所以要封装一个方法无疑还是方便客户端的调用,省的每次获取 ClassLoader 才能加载资源文件的麻烦!
public InputStream getResourceAsStream(String name) {
name = resolveName(name);
ClassLoader cl = getClassLoader0();
if (cl==null) {
// A system class.
return ClassLoader.getSystemResourceAsStream(name);
}
return cl.getResourceAsStream(name);
}

所以直接 getResourceAsStream 实际上内部也是调用 getClassLoader().getResourceAsStream

加载资源 Demo

image.png

public class Temp {
public static void main(String[] args) throws IOException {

URL resource = Temp.class.getClassLoader().getResource("./temp.txt");
// file:/D:/JavaProject/study-java/studySocket/target/classes/temp.txt

try (FileInputStream fis = new FileInputStream(resource.getPath());
InputStreamReader isr = new InputStreamReader(fis, StandardCharsets.UTF_8);
BufferedReader br = new BufferedReader(isr)) {

String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}
}
}

资源(Resource)接口

很多情况下自己写读取资源文件,都是通过 I/O 相关的类,File,InputStream,OutPutStream 这些,而 Spring 作为一个通用框架,肯定提供了资源读取的工具类

Spring 提供了一个 org.springframework.core.io.Resource 接口,同时 Spring 提供了若干 Resource 接口的实现类,这些实现类可以轻松地加载不同类型的底层资源,并提供了获取文件名、URL 地址以及资源内容的操作方法。

  1. UrlResource
  2. ClassPathResource
  3. FileSystemResource
  4. ServletContextResource
  5. InputStreamResource
  6. ByteArrayResource

可以指定不同的前缀来创建路径以从不同位置加载资源

前缀示例说明
classpath:classpath:com/myapp/config.xml从类路径加载
file:file:///data/config.xml从文件系统作为URL加载。
http:https://myserver/logo.png从URL加载
(none)/data/config.xml取决于底层的 ApplicationContext

ResourceLoaderAware 接口使用

它用于加载资源(例如类路径或文件系统资源)。

//Expose the ClassLoader used by this ResourceLoader.
ClassLoader getClassLoader()

//Return a Resource handle for the specified resource location.
Resource getResource(String location)

getResource() 方法将根据资源路径决定要实例化的 Resource 实现。 这个 ResourceLoader 需要实现 ResourceLoaderAware 接口。

ResourceLoader 有如下两种方法取得:

使用 ApplicationContext 的方式

在 Spring 中,所有应用程序上下文都实现 ResourceLoader 接口。因此,所有应用程序上下文都可用于获取资源实例。

这个 ApplicationContext 实现了 ApplicationContextAware 接口。

@Autowired
private ApplicationContext ctx;

Resource banner = ctx.getResource("file:c:/temp/filesystemdata.txt");

// 或者
Resource banner = ctx.getResource("classpath:filesystemdata.txt");

下同

使用 ResourceLoader 的方式

@Autowired
private ResourceLoader resourceLoader;

Resource banner = resourceLoader.getResource("file:c:/temp/filesystemdata.txt");

指定使用工具类

上面使用自动注入的方式取得的 ResourceLoader 会自动根据输入的地址选择合适的工具类加载资源,如果想要自己指定也可以使用如下两种方式(其它就不列了,大同小异)

使用 ClassPathResource 类读取

@RestController
public class TestController {

@RequestMapping("testFile")
public String testFile() throws IOException {
// ClassPathResource类的构造方法接收路径名称,自动去classpath路径下找文件
ClassPathResource classPathResource = new ClassPathResource("test.txt");

// 获得File对象,当然也可以获取输入流对象
File file = classPathResource.getFile();

BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
StringBuilder content = new StringBuilder();
String line = null;
while ((line = bufferedReader.readLine()) != null) {
content.append(line);
}

return content.toString();
}
}

使用 FileSystemResource 类读取文件

FileSystemResource 这个类在找文件的时候就是按照给定的路径名去找,默认的当前目录就是项目根目录。

使用该类来查找文件时,需要保证文件路径完全正确,另外,在代码中将路径写死是一个不好的习惯,特别是一个文件的路径在不同的主机上的位置(层级目录)不一定相同,所以我们开发过程中很少使用这种方式。

FileSystemResource 的用法和 ClassPathResource 的用法相似,因为他们都继承了 AbstractResource 这个抽象类。

public class FileTest extends ApplicationConfigTest {

@Test
public void testFile() throws IOException {

FileSystemResource resource = new FileSystemResource("./");
System.out.println(resource.getFile().getAbsolutePath());
// 传入当前路径,获得的是项目根目录:/Users/ganlixin/code/Spring/demo/example/.

// 传入根目录路径,获得的就是操作系统的根目录
resource = new FileSystemResource("/");
System.out.println(resource.getFile().getAbsolutePath()); // 输出 /

// 获取单元测试resources目录下的test.txt,需要指定详细的路径
resource = new FileSystemResource("src/test/resources/test.txt");
final File file = resource.getFile();

final BufferedReader bufferedReader = new BufferedReader(new FileReader(file));
String line = null;
while ((line = bufferedReader.readLine()) != null) {
System.out.println(line);
}
}
}

无法读取打包到 jar 包里面的资源

在开发环境中,使用 ResourceUtils.getFile(“classpath:static/files/8k.wav”) 能读取到 File ,结果打包发布后,读取不了。主要问题是 ClassPathResource 只能读文件,jar 包中的已经不算文件了,所以 getFile 会报错,只能使用 getInputStream

如果要在 Spring Boot JAR 中从类路径加载文件,则必须使用该 resource.getInputStream() 方法将其作为 InputStream 进行检索。如果尝试使用,resource.getFile() 将收到错误,因为 Spring 尝试访问文件系统路径,但它无法访问 JAR 中的路径。

解决方法

//方式1:
InputStream inputStream = getClass().getClassLoader().getResourceAsStream(fileName)



//方式2:
public static String getFile(String filePath) {
ResourceLoader resourceLoader = new DefaultResourceLoader();
Resource resource=resourceLoader.getResource("classpath:"+filePath);
StringBuffer strBuff = new StringBuffer();
try (InputStream inputStream = resource.getInputStream()) {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String string = null;
while ((string = bufferedReader.readLine()) != null) {
strBuff.append(string);
}
} catch (Exception e) {
e.printStackTrace();
}
return strBuff.toString();
}

使用示例:

private String jsonFile;


@Autowired
private ApplicationContext ctx;

/**
* 构造方法执行之后,调用此方法
* 这里读取文件
*/
@PostConstruct
public void init() throws IOException {
Resource resource = ctx.getResource("classpath:testData.json");
StringBuilder content = new StringBuilder();

try (InputStream inputStream = resource.getInputStream()) {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String string = null;
while ((string = bufferedReader.readLine()) != null) {
content.append(string);
}
} catch (Exception e) {
e.printStackTrace();
}

log.info("启动时加载了文件");
jsonFile = content.toString();
}

Reference

springboot读取jar包resources下的文件 参考资料 Spring项目读取resource下的文件 参考资料 Spring系列八:Spring 中读取文件-ResourceLoaderAware